#include "AutoFind.h"

#define HEADER_SIZE 16

static uint32_t bufferRead32be(uint8_t *buf) {
    return static_cast<uint32_t>((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]);
}

static uint16_t bufferRead16be(uint8_t *buf) {
    return static_cast<uint16_t>((buf[0] << 8) | buf[1]);
}

static uint64_t bufferRead64be(uint8_t *buf) {
    uint32_t msb = bufferRead32be(buf);
    uint32_t lsb = bufferRead32be(&buf[4]);
    return (static_cast<uint64_t>(msb) << 32) | lsb;
}

// The "meta" header length is 16 bytes:
// [. . . . . . . .|..|..|. . . .]. . . . . . . . . . . . . . . ...
//  <-------------><--><--><-----> <-----------------------------...
//        PTS        packet        raw packet
//                    size
//
bool AutoFind::recvPacket(SKPacket *packet) {

    uint8_t header[HEADER_SIZE];
    int32_t r = recvData(header, HEADER_SIZE);
    if (r < HEADER_SIZE) {
        return false;
    }
    uint64_t ptsFlags = bufferRead64be(header);
    uint32_t va = bufferRead32be(&header[8]);
    uint32_t len = bufferRead32be(&header[12]);

    ///zhou
    if(va != 0x7A686F75){
        return false;
    }
    if(!newPacket(packet,static_cast<int>(len))){
        return false;
    }
    r = recvData(packet->data, len);
    if (r < 0 || static_cast<uint32_t>(r) < len) {
        resetPacket(packet);
        return false;
    }
    packet->pts = ptsFlags;
    return true;
}

ThreadSafeQueue<SKPacket *>  send_queue;
ThreadSafeQueue<SKPacket *>  recv_queue;

///把data的引用交换给别人
bool AutoFind::pushPacket(SKPacket *packet) {

    SKPacket *data;
    data = (struct SKPacket *)malloc(sizeof(struct SKPacket));
    data->data = packet->data;
    data->size = packet->size;
    data->pts = packet->pts;
    packet->data = nullptr;
    recv_queue.Push(data);
    return true;
}

void SendAndRecvThread(AutoFind *autoFind){
    SKPacket *packet = nullptr;
    cJSON *root = NULL;
    ///cJSON_Delete Delete a cJSON entity and all subentities
    ///cJSON_free cJSON_Print(root)将cJSON对象渲染为格式化的JSON字符串
    CMDHandle *cmdHandle = new CMDHandle();
    cmdHandle->handleHello(NULL);
    for(;;){
        if(!send_queue.Empty()){
            packet = send_queue.Pop();
            autoFind->sendData((const char *)packet->data,static_cast<int32_t>(packet->size));
        }
        if(!recv_queue.Empty()){
            packet = recv_queue.Pop();
            root = cJSON_Parse((const char *)packet->data);
            if(!root){
                continue;
            }
            autoFind->handleCMD(root,cmdHandle);
            cJSON_Delete(root);
            root = NULL;
        }
        if(packet!= nullptr){
            free(packet->data);
            free(packet);
            packet = nullptr;
        }
        if(autoFind->getExit())
            break;
        std::this_thread::sleep_for(std::chrono::milliseconds (10));
    }
    delete cmdHandle;
}

static inline std::wstring ConvertFromUTF8(const std::string &str) {
    int wideLength = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
    if (wideLength == 0) {
        return L"";
    }

    std::wstring wideString(wideLength, 0);
    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wideString[0], wideLength);

    return wideString;
}

/**
 *
 * @param root
 * @return
 *
 * {
 *      "cmd":0,
 *
 * }
 */
bool AutoFind::handleCMD(cJSON *root,CMDHandle *cmdHandle) {
    cJSON *temp;
    temp = cJSON_GetObjectItem(root, "cmd");
    if(!cJSON_IsNumber(temp)){
        cmdHandle->handleError("cmd error");
        return false;
    }
    switch (temp->valueint) {
        case SocketCMD::hello:
            log_info("get cmd hello");
            temp = cJSON_GetObjectItem(root, "id");
            cmdHandle->handleHello(temp);
            break;
        case SocketCMD::cmd:
            log_info("get cmd cmd");
            temp = cJSON_GetObjectItem(root, "data");
            cmdHandle->handleCMD(std::string(cJSON_GetStringValue(temp)));
            break;
        case SocketCMD::ping:
            log_info("get cmd ping");
            temp = cJSON_GetObjectItem(root, "id");
            cmdHandle->handlePing(std::string(cJSON_GetStringValue(temp)));
            break;
        case SocketCMD::logs:
            log_info("get cmd log");
            temp = cJSON_GetObjectItem(root, "fileName");
            cmdHandle->handleDmesg(std::string(cJSON_GetStringValue(temp)));
            break;
        case SocketCMD::file:
            log_info("get cmd file");
            {
                temp = cJSON_GetObjectItem(root, "path");
                cJSON *dir = cJSON_GetObjectItem(root, "dir");
                if(!cJSON_IsNumber(dir)){
                    cmdHandle->handleError("dir type error!");
                    return false;
                }
                if(!cJSON_IsArray(temp)){
                    cmdHandle->handleError("path type error!");
                    return false;
                }
                cmdHandle->handleFiles(temp,dir->valueint);
            }
            break;
        case SocketCMD::test:
            cmdHandle->handleTest(root);
            break;
        case SocketCMD::error:
            log_info("get cmd error");
            break;
        default:
            log_error("get cmd error for:%d",temp->valueint);
            break;
    }


    return true;
}
